############################################################################
#
# Copyright (C) 2016 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of Qt Creator.
#
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
############################################################################

# This is a place to add your own dumpers for testing purposes.
# Any contents here will be picked up by GDB, LLDB, and CDB based
# debugging in Qt Creator automatically.

# NOTE: This file will get overwritten when updating Qt Creator.
#
# To add dumpers that don't get overwritten, copy this file here
# to a safe location outside the Qt Creator installation and
# make this location known to Qt Creator using the Debugger >
# Locals & Expressions > Extra Debugging Helpers setting.

# Example to display a simple type
# template<typename U, typename V> struct MapNode
# {
#     U key;
#     V data;
# }
#
# def qdump__MapNode(d, value):
#    d.putValue("This is the value column contents")
#    d.putExpandable()
#    if d.isExpanded():
#        with Children(d):
#            # Compact simple case.
#            d.putSubItem("key", value["key"])
#            # Same effect, with more customization possibilities.
#            with SubItem(d, "data")
#                d.putItem("data", value["data"])

# Check http://doc.qt.io/qtcreator/creator-debugging-helpers.html
# for more details or look at qttypes.py, stdtypes.py, boosttypes.py
# for more complex examples.

import numbers
import types
import traceback
from typing import *
from ctypes import c_longlong, c_double, cast
from dumper import Children, SubItem, UnnamedSubItem, DumperBase
from utils import DisplayFormat, TypeCode
import stdtypes

######################## Your code below #######################

PropDumper = Callable[[DumperBase], None]

def dValue(value: DumperBase.Value, name: Optional[str] = None) -> PropDumper:
    def dump(d: DumperBase) -> None:
        component = name
        if component is None or not component:
            component = value.name
        if component is None or not component:
            component = f'[{value.type.name}]'
        with SubItem(d, component):
            d.putSortGroup(100)
            d.putItem(value)
    return dump

def dString(text: str, name: str) -> PropDumper:
    def dump(d: DumperBase) -> None:
        with SubItem(d, name):
            d.putValue(text)
    return dump

class JSType:
    TYPE = 'panda::ecmascript::JSType'
    NAMES = [
        "INVALID",
        "JS_OBJECT",
        "JS_REALM",
        "JS_FUNCTION_BASE",
        "JS_FUNCTION",
        "JS_PROXY_REVOC_FUNCTION",
        "JS_PROMISE_REACTIONS_FUNCTION",
        "JS_PROMISE_EXECUTOR_FUNCTION",
        "JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION",
        "JS_GENERATOR_FUNCTION",
        "JS_ASYNC_FUNCTION",
        "JS_ASYNC_GENERATOR_FUNCTION",
        "JS_INTL_BOUND_FUNCTION",
        "JS_ASYNC_AWAIT_STATUS_FUNCTION",
        "JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION",
        "JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION",
        "JS_BOUND_FUNCTION",

        "JS_ERROR",
        "JS_EVAL_ERROR",
        "JS_RANGE_ERROR",
        "JS_REFERENCE_ERROR",
        "JS_TYPE_ERROR",
        "JS_URI_ERROR",
        "JS_SYNTAX_ERROR",

        "JS_REG_EXP",
        "JS_SET",
        "JS_MAP",
        "JS_WEAK_REF",
        "JS_WEAK_MAP",
        "JS_WEAK_SET",
        "JS_DATE",
        "JS_ITERATOR",
        "JS_FORIN_ITERATOR",
        "JS_MAP_ITERATOR",
        "JS_REG_EXP_ITERATOR",
        "JS_SET_ITERATOR",
        "JS_ARRAY_ITERATOR",
        "JS_STRING_ITERATOR",
        "JS_INTL",
        "JS_LOCALE",
        "JS_DATE_TIME_FORMAT",
        "JS_RELATIVE_TIME_FORMAT",
        "JS_NUMBER_FORMAT",
        "JS_COLLATOR",
        "JS_PLURAL_RULES",
        "FINALIZATION_REGISTRY",
        
        "JS_ARRAY_BUFFER",
        "JS_PROMISE",
        "JS_DATA_VIEW",
        "JS_ARGUMENTS",
        "JS_GENERATOR_OBJECT",
        "JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT",
        "JS_ASYNC_FUNC_OBJECT",
        "JS_ASYNC_GENERATOR_OBJECT",
        
        "JS_ARRAY",
        "JS_ARRAY_LIST",
        "JS_QUEUE",
        "JS_TYPED_ARRAY",
        "JS_INT8_ARRAY",
        "JS_UINT8_ARRAY",
        "JS_UINT8_CLAMPED_ARRAY",
        "JS_INT16_ARRAY",
        "JS_UINT16_ARRAY",
        "JS_INT32_ARRAY",
        "JS_UINT32_ARRAY",
        "JS_FLOAT32_ARRAY",
        "JS_FLOAT64_ARRAY",
        "JS_BIGINT64_ARRAY",
        "JS_BIGUINT64_ARRAY",
        "JS_PRIMITIVE_REF",
        "JS_GLOBAL_OBJECT",
        "JS_PROXY",
        
        "HCLASS",
        "STRING",
        "BIGINT",
        "TAGGED_ARRAY",
        "TAGGED_DICTIONARY",
        "LINKED_HASH_SET",
        "LINKED_HASH_MAP",
        "FREE_OBJECT_WITH_ONE_FIELD",
        "FREE_OBJECT_WITH_NONE_FIELD",
        "FREE_OBJECT_WITH_TWO_FIELD",
        "JS_NATIVE_POINTER",
        "GLOBAL_ENV",
        "ACCESSOR_DATA",
        "INTERNAL_ACCESSOR",
        "SYMBOL",
        "OBJECT_WRAPPER",
        "JS_GENERATOR_CONTEXT",
        "PROTOTYPE_HANDLER",
        "TRANSITION_HANDLER",
        "PROPERTY_BOX",
        "PROTO_CHANGE_MARKER",
        "PROTOTYPE_INFO",
        "TEMPLATE_MAP",
        "PROGRAM",
        "LEXICAL_FUNCTION",

        "PROMISE_CAPABILITY",
        "PROMISE_RECORD",
        "RESOLVING_FUNCTIONS_RECORD",
        "PROMISE_REACTIONS",
        "PROMISE_ITERATOR_RECORD",
        "MICRO_JOB_QUEUE",
        "PENDING_JOB",
        "FUNCTION_EXTRA_INFO",
        "COMPLETION_RECORD",
        "ECMA_MODULE",
        "CLASS_INFO_EXTRACTOR",
    ]
    NUM_NAMES = dict(enumerate(NAMES))
    NAME_NUMS = {v: k for k, v in NUM_NAMES.items()}

def deref(value: DumperBase.Value) -> DumperBase.Value:
    refs = (
        TypeCode.Pointer,
        TypeCode.Reference,
        TypeCode.RValueReference,
    )
    return value.dereference() if value.type.code in refs else value


class ValueClass:
    def __init__(self, value: DumperBase.Value):
        if value.laddress == 0xFFFFFFFFFFFFFFFF:
            value.laddress = None
        self.value: DumperBase.Value = deref(value)

    @property
    def dumper(self) -> DumperBase:
        return self.value.dumper


class BaseClass(ValueClass):
    def __init__(self, value):
        ValueClass.__init__(self, value)
        self._flags_ = value['flags_'].integer()

    def isFlag(self, flag) -> bool:
        return self._flags_ & flag != 0
    
    # @property
    # def managed_object(self):
    #     return self.dumper.createPointerValue(self.value['managed_value_'].integer(), ObjectHeader.TYPE)

class HClass(BaseClass):
    TYPE: str = 'panda::HClass'
    HCLASS = 1 << 1
    STRING = HCLASS << 1
    ARRAY = STRING << 1
    BIGINT = ARRAY << 1
    NATIVE_POINTER = BIGINT << 1
    IS_DICTIONARY_ARRAY = NATIVE_POINTER << 1
    IS_BUILTINS_CTOR = IS_DICTIONARY_ARRAY << 1
    IS_CALLABLE = IS_BUILTINS_CTOR << 1
    IS_FREE_OBJECT = IS_CALLABLE << 1

    def __init__(self, value):
        BaseClass.__init__(self, value)
    
    @property
    def isHClass(self) -> bool:
        return self.isFlag(self.HCLASS)
    
    @property
    def isString(self) -> bool:
        return self.isFlag(self.STRING)
    
    @property
    def isArray(self) -> bool:
        return self.isFlag(self.ARRAY)
     
    @property
    def isBigInt(self) -> bool:
        return self.isFlag(self.BIGINT)
    
    @property
    def isNativePointer(self) -> bool:
        return self.isFlag(self.NATIVE_POINTER)
    
    @property
    def isDictionary(self) -> bool:
        return self.isFlag(self.IS_DICTIONARY_ARRAY)
    
    @property
    def isBuiltinsConstructor(self) -> bool:
        return self.isFlag(self.IS_BUILTINS_CTOR)
    
    @property
    def isCallable(self) -> bool:
        return self.isFlag(self.IS_CALLABLE)
    
    @property
    def isFreeObject(self) -> bool:
        return self.isFlag(self.IS_FREE_OBJECT)
   
    @property
    def isFreeObject(self) -> bool:
        return self.isFlag(self.IS_FREE_OBJECT)
    

class ObjectHeader(ValueClass):
    TYPE: str = 'panda::ObjectHeader'

    def __init__(self, value):
        super().__init__(value)

    @property
    def class_word_(self):
        return self.value['class_word_'];

    @property
    def class_word(self) -> DumperBase.Value:
        return self.dumper.createPointerValue(self.class_word_.integer(), HClass.TYPE)


class TaggedValue(ValueClass):
    TYPE = 'panda::coretypes::TaggedValue'
    def __init__(self, value):
        super().__init__(value)
        self.HEAP_OBJECT_TYPE = ObjectHeader.TYPE
        try:
            self.value_ = value['value_']
        except BaseException as e:
            pass
            # self.value_ = value
        self.int = self.value_.integer()
        self.TAG_OBJECT = 0
        self.TAG_MASK = 0xFFFF000000000000
        self.TAG_INT = self.TAG_MASK
        self.TAG_WEAK_MASK = 1
        self.TAG_SPECIAL_MASK = 0xFF
        self.TAG_SPECIAL_VALUE = 0x02
        self.TAG_UNDEFINED = 0x08
        self.TAG_EXCEPTION = 0x10
        self.VALUE_HOLE = 0
        self.VALUE_NULL = 2
        self.VALUE_FALSE = 6
        self.VALUE_TRUE = 7
        self.VALUE_UNDEFINED = self.TAG_OBJECT or self.TAG_SPECIAL_VALUE or self.TAG_UNDEFINED
        self.VALUE_EXCEPTION = self.TAG_OBJECT or self.TAG_SPECIAL_VALUE or self.TAG_EXCEPTION
        self.DOUBLE_ENCODE_OFFSET = 1 << 48
     
    @property
    def isObject(self):
        return (self.int & self.TAG_MASK) == self.TAG_OBJECT
     
    @property
    def isHole(self):
        return self.int == self.VALUE_HOLE
   
    @property
    def isSpecial(self):
        return ((self.int & ~self.TAG_SPECIAL_MASK) == 0) and (((self.int & self.TAG_SPECIAL_VALUE) != 0) or self.isHole);
   
    @property
    def isHeap(self):
        return self.isObject and not self.isSpecial
    
    @property
    def isInt(self):
        return (self.int & self.TAG_MASK) == self.TAG_INT
    
    @property
    def address(self):
        return self.int & ~self.TAG_WEAK_MASK if self.isHeap else None

    @property
    def isWeak(self):
        return self.int & self.TAG_WEAK_MASK == 1 and self.isHeap
    
    @property
    def isNull(self):
        return self.int == self.VALUE_NULL
    
    @property
    def isNumber(self):
        return not self.isObject
    
    @property
    def isFalse(self):
        return self.int == self.VALUE_FALSE
    
    @property
    def isTrue(self):
        return self.int == self.VALUE_TRUE
    
    @property
    def isBoolean(self):
        return self.isFalse or self.isTrue

    @property
    def isDouble(self):
        return not self.isInt and not self.isObject

    @property
    def isUndefined(self):
        return self.int == self.VALUE_UNDEFINED
    
    @property
    def isException(self):
        return self.int == self.VALUE_EXCEPTION
    
    @property
    def double(self):
        ll = c_longlong(self.int - self.DOUBLE_ENCODE_OFFSET)
        return cast(ll, c_double)
    
    @property
    def heapObject(self) -> DumperBase.Value:
        if (self.isHeap):
            return self.dumper.createPointerValue(self.address, self.HEAP_OBJECT_TYPE)

    def getValue(self):
        if (self.isInt):
            return self.dumper.createValue(self.value.data(), 'int')
        if (self.isDouble):
            return self.dumper.createValue(self.double, 'double')
        # if (self.isSpecial):
        #     return self.dumper.createValue('<SPECIAL>', 'string')
        if (self.isHeap):
            return self.heapObject
        return self.value['value_']

class TaggedObject(ObjectHeader):
    TYPE = 'panda::ecmascript::TaggedObject'

    def __init__(self, value):
        super().__init__(value)
        self.basePattern = ''
        self.pattern = '{ObjectHeader}'
        self._jshClass = None
        self._accessors = []
        self._accessorsOffset = 8

    def _appendAccessor(self, name: str, type: DumperBase.Type, size: int) -> DumperBase.Value:
        address = self.value.address() + self._accessorsOffset
        # DumperBase.warn(f'ADDRESS: {address} SIZE: {size} TYPE: {type}')
        self._accessorsOffset += size
        value = self.dumper.createValue(address, type)
        self._accessors += [ value, ]
        value.name = name
        return value

    def _appendAccessorArray(self, name: str, typeName, count, size = 0) -> DumperBase.Value:
        targetType = self.dumper.createType(typeName)
        type = self.dumper.createArrayType(targetType, count)
        if size == 0:
            size = type.size()
        return self._appendAccessor(name, type, size * count)

    def _appendAccessorValue(self, name: str, t: Union[str, type, None] = None, size: int = 0) -> DumperBase.Value:
        if t is None:
            t = JSTaggedValue
        if isinstance(t, str):
            tn: DumperBase.Type = self.dumper.createType(t)
            if size == 0:
                size = tn.size()
        else:
            tn: DumperBase.Type = self.dumper.createType(t.TYPE)
            if size == 0:
                try:
                    size = t.SIZE
                except:
                    size = tn.size()
        if size > 0:
            return self._appendAccessor(name, tn, size)
        else:
            DumperBase.warn(f'ERROR: {t} not has SIZE')

    def patternOffeset(self):
        (_, size, _) = self.dumper.describeStruct(self.basePattern)
        return size

    @property
    def jshClass(self) -> DumperBase.Value:
        return self.dumper.createPointerValue(self.managed_object.integer(), JSHClass.TYPE)
    
    @property
    def managed_object(self) -> DumperBase.Value:
        return self.class_word.dereference()['managed_object_']
    
    @property
    def ObjectType(self):
        return JSHClass(self.jshClass).ObjectType
    
    def getJSHClass(self) -> 'JSHClass':
        if self._jshClass is None or self.jshClass is None:
            self._jshClass = JSHClass(self.jshClass)
        return self._jshClass

    def getJSType(self) -> DumperBase.Value:
        j = self.getJSHClass()
        return j.ObjectType if j is not None else None
    
    def getObjectType(self) -> str:
        j = self.getJSType()
        return jsTypeClass(j.integer()) if j is not None else None
    
    def getDynamicThis(self) -> DumperBase.Value:
        t = self.getObjectType()
        return self.value.cast(t) if t is not None else self.value
    

class JSTaggedValue(TaggedValue):
    TYPE = 'panda::ecmascript::JSTaggedValue'
    SIZE = int(8)
    def __init__(self, value):
        TaggedValue.__init__(self, value)
        self.HEAP_OBJECT_TYPE = TaggedObject.TYPE
    
    @property
    def heapObject(self):
        if self.isHeap:
            obj = self.dumper.createPointerValue(self.address, TaggedObject.TYPE)
            obj.name = 'value'
            return obj


class JSHClass(ObjectHeader):
    TYPE = 'panda::ecmascript::JSHClass'
    def __init__(self, value):
        ObjectHeader.__init__(self, value)
        self.hClass = self.dumper.createValue(self.value.address() + 8, HClass.TYPE)
        (jstype, _, _, _, self._bitField1) = self.hClass['data_'].split('{uint8_t}' * 4 + '{uint32_t}')
        self._jstype = jstype.cast(JSType.TYPE)
    
    @property
    def ObjectType(self) -> DumperBase.Value:
        return self._jstype


class TaggedArray(TaggedObject):
    TYPE = 'panda::ecmascript::TaggedArray'
    def __init__(self, value):
        super().__init__(value)
        self.Length = self._appendAccessorValue('Length', 'uint32_t', 8)
        self.Array = self._appendAccessorArray('Array', 'uint32_t', self.Length.integer())


class EcmaString(TaggedObject):
    TYPE = 'panda::ecmascript::EcmaString'
    def __init__(self, value):
        super().__init__(value)


class ECMAObject(TaggedObject):
    TYPE = 'panda::ecmascript::ECMAObject'
    def __init__(self, value):
        super().__init__(value)
        self.Hash = self._appendAccessorValue('Hash')


class JSObject(ECMAObject):
    TYPE = 'panda::ecmascript::JSObject'
    def __init__(self, value):
        super().__init__(value)
        self.Properties = self._appendAccessorValue('Properties')
        self.Elements = self._appendAccessorValue('Elements')


class GlobalEnv(TaggedObject):
    TYPE = 'panda::ecmascript::GlobalEnv'
    def __init__(self, value):
        super().__init__(value)


class JSFunctionBase(JSObject):
    TYPE = 'panda::ecmascript::JSFunctionBase'
    def __init__(self, value):
        super().__init__(value)
        self.Method = self._appendAccessorValue('Method', f'{JSMethod.TYPE}*')


class JSFunction(JSFunctionBase):
    TYPE = 'panda::ecmascript::JSFunction'
    def __init__(self, value):
        super().__init__(value)
        self.ProtoOrDynClass = self._appendAccessorValue('ProtoOrDynClass')
        self.LexicalEnv = self._appendAccessorValue('LexicalEnv')
        self.HomeObject = self._appendAccessorValue('HomeObject')
        self.FunctionInfoFlag = self._appendAccessorValue('FunctionInfoFlag')
        self.FunctionExtraInfo = self._appendAccessorValue('FunctionExtraInfo')
        self.ConstantPool = self._appendAccessorValue('ConstantPool')
        self.ProfileTypeInfo = self._appendAccessorValue('ProfileTypeInfo')


class JSBoundFunction(JSFunctionBase):
    TYPE = 'panda::ecmascript::JSBoundFunction'
    def __init__(self, value):
        super().__init__(value)


class JSSet(JSObject):
    TYPE = 'panda::ecmascript::JSSet'
    def __init__(self, value):
        super().__init__(value)


class JSMap(JSObject):
    TYPE = 'panda::ecmascript::JSMap'
    def __init__(self, value):
        super().__init__(value)


class JSWeakRef(JSObject):
    TYPE = 'panda::ecmascript::JSWeakRef'
    def __init__(self, value):
        super().__init__(value)


class JSWeakSet(JSObject):
    TYPE = 'panda::ecmascript::JSWeakSet'
    def __init__(self, value):
        super().__init__(value)


class JSWeakMap(JSObject):
    TYPE = 'panda::ecmascript::JSWeakMap'
    def __init__(self, value):
        super().__init__(value)


class JSRegExp(JSObject):
    TYPE = 'panda::ecmascript::JSRegExp'
    def __init__(self, value):
        super().__init__(value)


class JSDate(JSObject):
    TYPE = 'panda::ecmascript::JSDate'
    def __init__(self, value):
        super().__init__(value)


class JSArray(JSObject):
    TYPE = 'panda::ecmascript::JSArray'
    def __init__(self, value):
        super().__init__(value)


class BigInt(TaggedValue):
    TYPE = 'panda::ecmascript::BigInt'
    def __init__(self, value):
        super().__init__(value)


class JSTypedArray(JSObject):
    TYPE = 'panda::ecmascript::JSTypedArray'
    def __init__(self, value):
        super().__init__(value)


class JSProxy(ECMAObject):
    TYPE = 'panda::ecmascript::JSProxy'
    def __init__(self, value):
        super().__init__(value)


class JSPrimitiveRef(JSObject):
    TYPE = 'panda::ecmascript::JSPrimitiveRef'
    def __init__(self, value):
        super().__init__(value)


class JSSymbol(TaggedObject):
    TYPE = 'panda::ecmascript::JSSymbol'
    def __init__(self, value):
        super().__init__(value)


class JSDataView(JSObject):
    TYPE = 'panda::ecmascript::JSDataView'
    def __init__(self, value):
        super().__init__(value)


class JSArrayBuffer(JSObject):
    TYPE = 'panda::ecmascript::JSArrayBuffer'
    def __init__(self, value):
        super().__init__(value)


class Record(TaggedObject):
    TYPE = 'panda::ecmascript::Record'
    def __init__(self, value):
        super().__init__(value)


class PromiseReaction(Record):
    TYPE = 'panda::ecmascript::PromiseReaction'
    def __init__(self, value):
        super().__init__(value)


class PromiseCapability(Record):
    TYPE = 'panda::ecmascript::PromiseCapability'
    def __init__(self, value):
        super().__init__(value)


class PromiseIteratorRecord(Record):
    TYPE = 'panda::ecmascript::PromiseIteratorRecord'
    def __init__(self, value):
        super().__init__(value)


class PromiseRecord(Record):
    TYPE = 'panda::ecmascript::PromiseRecord'
    def __init__(self, value):
        super().__init__(value)


class ResolvingFunctionsRecord(Record):
    TYPE = 'panda::ecmascript::ResolvingFunctionsRecord'
    def __init__(self, value):
        super().__init__(value)


class JSPromise(JSObject):
    TYPE = 'panda::ecmascript::JSPromise'
    def __init__(self, value):
        super().__init__(value)


class JSPromiseReactionsFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSPromiseReactionsFunction'
    def __init__(self, value):
        super().__init__(value)


class JSPromiseExecutorFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSPromiseExecutorFunction'
    def __init__(self, value):
        super().__init__(value)


class JSPromiseAllResolveElementFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSPromiseAllResolveElementFunction'
    def __init__(self, value):
        super().__init__(value)


class MicroJobQueue(Record):
    TYPE = 'panda::ecmascript::job::MicroJobQueue'
    def __init__(self, value):
        super().__init__(value)


class PendingJob(Record):
    TYPE = 'panda::ecmascript::job::PendingJob'
    def __init__(self, value):
        super().__init__(value)


class CompletionRecord(Record):
    TYPE = 'panda::ecmascript::CompletionRecord'
    def __init__(self, value):
        super().__init__(value)


class JSProxyRevocFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSProxyRevocFunction'
    def __init__(self, value):
        super().__init__(value)


class JSAsyncFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSAsyncFunction'
    def __init__(self, value):
        super().__init__(value)


class JSAsyncGeneratorFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSAsyncGeneratorFunction'
    def __init__(self, value):
        super().__init__(value)


class JSAsyncAwaitStatusFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSAsyncAwaitStatusFunction'
    def __init__(self, value):
        super().__init__(value)


class JSAsyncGeneratorResolveNextFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSAsyncGeneratorResolveNextFunction'
    def __init__(self, value):
        super().__init__(value)


class JSAsyncFromSyncIteratorValueUnwrapFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSAsyncFromSyncIteratorValueUnwrapFunction'
    def __init__(self, value):
        super().__init__(value)


class JSGeneratorFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSGeneratorFunction'
    def __init__(self, value):
        super().__init__(value)


class JSIntlBoundFunction(JSFunction):
    TYPE = 'panda::ecmascript::JSIntlBoundFunction'
    def __init__(self, value):
        super().__init__(value)


class JSForInIterator(JSObject):
    TYPE = 'panda::ecmascript::JSForInIterator'
    def __init__(self, value):
        super().__init__(value)


class JSMapIterator(JSObject):
    TYPE = 'panda::ecmascript::JSMapIterator'
    def __init__(self, value):
        super().__init__(value)


class JSSetIterator(JSObject):
    TYPE = 'panda::ecmascript::JSSetIterator'
    def __init__(self, value):
        super().__init__(value)


class JSRegExpIterator(JSObject):
    TYPE = 'panda::ecmascript::JSRegExpIterator'
    def __init__(self, value):
        super().__init__(value)


class JSArrayIterator(JSObject):
    TYPE = 'panda::ecmascript::JSArrayIterator'
    def __init__(self, value):
        super().__init__(value)


class JSStringIterator(JSObject):
    TYPE = 'panda::ecmascript::JSStringIterator'
    def __init__(self, value):
        super().__init__(value)


class PrototypeHandler(TaggedObject):
    TYPE = 'panda::ecmascript::PrototypeHandler'
    def __init__(self, value):
        super().__init__(value)


class TransitionHandler(TaggedObject):
    TYPE = 'panda::ecmascript::TransitionHandler'
    def __init__(self, value):
        super().__init__(value)


class PropertyBox(TaggedObject):
    TYPE = 'panda::ecmascript::PropertyBox'
    def __init__(self, value):
        super().__init__(value)


class JSRealm(JSObject):
    TYPE = 'panda::ecmascript::JSRealm'
    def __init__(self, value):
        super().__init__(value)


class JSIntl(JSObject):
    TYPE = 'panda::ecmascript::JSIntl'
    def __init__(self, value):
        super().__init__(value)


class JSLocale(JSObject):
    TYPE = 'panda::ecmascript::JSLocale'
    def __init__(self, value):
        super().__init__(value)


class JSDateTimeFormat(JSObject):
    TYPE = 'panda::ecmascript::JSDateTimeFormat'
    def __init__(self, value):
        super().__init__(value)


class JSRelativeTimeFormat(JSObject):
    TYPE = 'panda::ecmascript::JSRelativeTimeFormat'
    def __init__(self, value):
        super().__init__(value)


class JSNumberFormat(JSObject):
    TYPE = 'panda::ecmascript::JSNumberFormat'
    def __init__(self, value):
        super().__init__(value)


class JSCollator(JSObject):
    TYPE = 'panda::ecmascript::JSCollator'
    def __init__(self, value):
        super().__init__(value)


class JSPluralRules(JSObject):
    TYPE = 'panda::ecmascript::JSPluralRules'
    def __init__(self, value):
        super().__init__(value)


class JSGeneratorObject(JSObject):
    TYPE = 'panda::ecmascript::JSGeneratorObject'
    def __init__(self, value):
        super().__init__(value)


class JSAsyncFuncObject(JSGeneratorObject):
    TYPE = 'panda::ecmascript::JSAsyncFuncObject'
    def __init__(self, value):
        super().__init__(value)


class GeneratorContext(TaggedObject):
    TYPE = 'panda::ecmascript::GeneratorContext'
    def __init__(self, value):
        super().__init__(value)


class ProtoChangeDetails(TaggedObject):
    TYPE = 'panda::ecmascript::ProtoChangeDetails'
    def __init__(self, value):
        super().__init__(value)


class ProtoChangeMarker(TaggedObject):
    TYPE = 'panda::ecmascript::ProtoChangeMarker'
    def __init__(self, value):
        super().__init__(value)


class Program(ECMAObject):
    TYPE = 'panda::ecmascript::Program'
    def __init__(self, value):
        super().__init__(value)


class LexicalFunction(ECMAObject):
    TYPE = 'panda::ecmascript::LexicalFunction'
    def __init__(self, value):
        super().__init__(value)


class JSFunctionExtraInfo(TaggedObject):
    TYPE = 'panda::ecmascript::JSFunctionExtraInfo'
    def __init__(self, value):
        super().__init__(value)


class EcmaModule(ECMAObject):
    TYPE = 'panda::ecmascript::EcmaModule'
    def __init__(self, value):
        super().__init__(value)


class ClassInfoExtractor(TaggedObject):
    TYPE = 'panda::ecmascript::ClassInfoExtractor'
    def __init__(self, value):
        super().__init__(value)


class JSArrayList(JSObject):
    TYPE = 'panda::ecmascript::JSArrayList'
    def __init__(self, value):
        super().__init__(value)


class Method(ValueClass):
    TYPE = 'panda::Method'
    def __init__(self, value):
        super().__init__(value)


class JSMethod(Method):
    TYPE = 'panda::ecmascript::JSMethod'
    def __init__(self, value):
        super().__init__(value)


JSTYPE_CLASSES = {
    'HCLASS': JSHClass,
    'TAGGED_ARRAY': TaggedArray,
    'LINKED_HASH_SET': TaggedArray,
    'LINKED_HASH_MAP': TaggedArray,
    'TAGGED_DICTIONARY': TaggedArray,
    'TEMPLATE_MAP': TaggedArray,
    'STRING': EcmaString,
    'JS_NATIVE_POINTER': None,
    'JS_OBJECT': JSObject,
    'JS_GLOBAL_OBJECT': JSObject,
    'JS_ERROR': JSObject,
    'JS_EVAL_ERROR': JSObject,
    'JS_RANGE_ERROR': JSObject,
    'JS_TYPE_ERROR': JSObject,
    'JS_REFERENCE_ERROR': JSObject,
    'JS_URI_ERROR': JSObject,
    'JS_SYNTAX_ERROR': JSObject,
    'JS_ARGUMENTS': JSObject,
    'JS_FUNCTION_BASE': JSObject,
    'GLOBAL_ENV': GlobalEnv,
    'ACCESSOR_DATA': None,
    'JS_FUNCTION': JSFunction,
    'JS_BOUND_FUNCTION': JSBoundFunction,
    'JS_SET': JSSet,
    'JS_MAP': JSMap,
    'JS_WEAK_REF': JSWeakRef,
    'JS_WEAK_SET': JSWeakSet,
    'JS_WEAK_MAP': JSWeakMap,
    'JS_REG_EXP': JSRegExp,
    'JS_DATE': JSDate,
    'JS_ARRAY': JSArray,
    'BIGINT': BigInt,
    'JS_TYPED_ARRAY': JSTypedArray,
    'JS_INT8_ARRAY': JSTypedArray,
    'JS_UINT8_ARRAY': JSTypedArray,
    'JS_UINT8_CLAMPED_ARRAY': JSTypedArray,
    'JS_INT16_ARRAY': JSTypedArray,
    'JS_UINT16_ARRAY': JSTypedArray,
    'JS_INT32_ARRAY': JSTypedArray,
    'JS_UINT32_ARRAY': JSTypedArray,
    'JS_FLOAT32_ARRAY': JSTypedArray,
    'JS_FLOAT64_ARRAY': JSTypedArray,
    'JS_PROXY': JSProxy,
    'JS_PRIMITIVE_REF': JSPrimitiveRef,
    'SYMBOL': JSSymbol,
    'JS_DATA_VIEW': JSDataView,
    'JS_ARRAY_BUFFER': JSArrayBuffer,
    'PROMISE_REACTIONS': PromiseReaction,
    'PROMISE_CAPABILITY': PromiseCapability,
    'PROMISE_ITERATOR_RECORD': PromiseIteratorRecord,
    'PROMISE_RECORD': PromiseRecord,
    'RESOLVING_FUNCTIONS_RECORD': ResolvingFunctionsRecord,
    'JS_PROMISE': JSPromise,
    'JS_PROMISE_REACTIONS_FUNCTION': JSPromiseReactionsFunction,
    'JS_PROMISE_EXECUTOR_FUNCTION': JSPromiseExecutorFunction,
    'JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION': JSPromiseAllResolveElementFunction,
    'MICRO_JOB_QUEUE': MicroJobQueue,
    'PENDING_JOB': PendingJob,
    'COMPLETION_RECORD': CompletionRecord,
    'JS_PROXY_REVOC_FUNCTION': JSProxyRevocFunction,
    'JS_ASYNC_FUNCTION': JSAsyncFunction,
    'JS_ASYNC_GENERATOR_FUNCTION': JSAsyncGeneratorFunction,
    'JS_ASYNC_AWAIT_STATUS_FUNCTION': JSAsyncAwaitStatusFunction,
    'JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION': JSAsyncGeneratorResolveNextFunction,
    'JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION': JSAsyncFromSyncIteratorValueUnwrapFunction,
    'JS_GENERATOR_FUNCTION': JSGeneratorFunction,
    'JS_INTL_BOUND_FUNCTION': JSIntlBoundFunction,
    'JS_ITERATOR': None,
    'JS_FORIN_ITERATOR': JSForInIterator,
    'JS_MAP_ITERATOR': JSMapIterator,
    'JS_SET_ITERATOR': JSSetIterator,
    'JS_REG_EXP_ITERATOR': JSRegExpIterator,
    'JS_ARRAY_ITERATOR': JSArrayIterator,
    'JS_STRING_ITERATOR': JSStringIterator,
    'PROTOTYPE_HANDLER': PrototypeHandler,
    'TRANSITION_HANDLER': TransitionHandler,
    'PROPERTY_BOX': PropertyBox,
    'JS_REALM': JSRealm,
    'JS_INTL': JSIntl,
    'JS_LOCALE': JSLocale,
    'JS_DATE_TIME_FORMAT': JSDateTimeFormat,
    'JS_RELATIVE_TIME_FORMAT': JSRelativeTimeFormat,
    'JS_NUMBER_FORMAT': JSNumberFormat,
    'JS_COLLATOR': JSCollator,
    'JS_PLURAL_RULES': JSPluralRules,
    'JS_GENERATOR_OBJECT': JSGeneratorObject,
    'JS_ASYNC_FUNC_OBJECT': JSAsyncFuncObject,
    'JS_GENERATOR_CONTEXT': GeneratorContext,
    'PROTOTYPE_INFO': ProtoChangeDetails,
    'PROTO_CHANGE_MARKER': ProtoChangeMarker,
    'PROGRAM': Program,
    'LEXICAL_FUNCTION': LexicalFunction,
    'FUNCTION_EXTRA_INFO': JSFunctionExtraInfo,
    'ECMA_MODULE': EcmaModule,
    'CLASS_INFO_EXTRACTOR': ClassInfoExtractor,
    'JS_ARRAY_LIST': JSArrayList,
    'FREE_OBJECT_WITH_NONE_FIELD': None,
    'FREE_OBJECT_WITH_ONE_FIELD': None,
    'FREE_OBJECT_WITH_TWO_FIELD': None,
    'INTERNAL_ACCESSOR': None,
}


def dumpValueHelper(d: DumperBase, value: DumperBase.Value, valueFuncs: Iterable[PropDumper] = None):
    if value.laddress is not None:
        d.putAddress(value.laddress)
    else:
        d.putValue(value.data())
    d.putExpandable()
    if d.isExpanded():
        with Children(d):
            if value.laddress is not None:
                d.putFields(value)
            try:
                if valueFuncs is not None:
                    for valueDumper in valueFuncs:
                        try:
                            valueDumper(d)
                        except Exception as e:
                            d.warn(f'ERROR: {e}')
            except Exception as e:
                d.warn(f'ERROR: {e}')

def dumpValueClass(d: DumperBase, obj: ValueClass, valueFuncs: Iterable[PropDumper] = None):
    dumpValueHelper(d, obj.value, valueFuncs)

def jsTypeClass(type) -> str:
    if isinstance(type, numbers.Number):
        type = JSType.NUM_NAMES[type]
    return JSTYPE_CLASSES[type].TYPE


def qdump__panda__ObjectHeader(d, value):
    obj = ObjectHeader(value)
    props = [dValue(obj.class_word, 'class_word')]
    dumpValueClass(d, obj, props)


def qdump__panda__coretypes__TaggedValue(d, value):
    obj = TaggedValue(value)
    def dumpHandler(d: DumperBase):
        boolProps = [
            ('Heap', obj.isHeap),
            ('Int', obj.isInt),
            ('Hole', obj.isHole),
            ('Spectial', obj.isSpecial),
            ('Weak', obj.isWeak),
            ('Null', obj.isNull),
            ('Number', obj.isNumber),
            ('Boolean', obj.isBoolean),
            ('Double', obj.isDouble),
            ('Undefined', obj.isUndefined),
            ('Exception', obj.isException),
        ]
        with SubItem(d, 'properties'):
            d.putValue(' | '.join([n for (n, v) in boolProps if v]))
            d.putExpandable()
            if (d.isExpanded()):
                with Children(d):
                    [d.putBoolItem(f'is{n}', v) for (n, v) in boolProps]
    props = [
        dumpHandler,
    ]
    v = obj.getValue()
    if v is not None:
        props.append(dValue(v))
    dumpValueClass(d, TaggedValue(value), props)


def qdump__panda__ecmascript__JSTaggedValue(d, value):
    try:
        obj = JSTaggedValue(value)
        DumperBase.warn(f'VALUE: {value}')
        props = [
            dValue(obj.heapObject, 'heapObject') if obj.isHeap else dValue(obj.getValue(), 'value'),
        ]
        dumpValueClass(d, obj, props)
    except RuntimeError as e:
        DumperBase.warn(e)

def qdump__panda__ecmascript__ECMAObject(d, value):
    obj = ECMAObject(value)
    props = [
        lambda d: dValue(obj.Hash)(d),
    ]
    dumpValueClass(d, obj, props)


def qdump__panda__ecmascript__JSObject(d, value):
    obj = JSObject(value)
    props = [
        lambda d: dValue(obj.Properties)(d),
        lambda d: dValue(obj.Elements)(d),
    ]
    dumpValueClass(d, obj, props)


def qdump__panda__ecmascript__TaggedObject(d, value):
    obj = TaggedObject(value)
    props = [
        lambda d: dValue(obj.jshClass.dereference(), '[JSHClass]')(d),
        lambda d: dValue(obj.ObjectType, 'ObjectType')(d),
        lambda d: dValue(obj.getDynamicThis(), '[this]')(d),
    ]
    dumpValueClass(d, obj, props)


def qdump__panda__ecmascript__TaggedArray(d, value):
    obj = TaggedArray(value)
    props = [
        dValue(obj.Length),
        dValue(obj.Array),
    ]
    dumpValueClass(d, obj, props)


def qdump__panda__ecmascript__JSHClass(d, value):
    obj = JSHClass(value)
    props = [
        dValue(obj.hClass, '[HClass]'),
        dValue(obj.ObjectType, 'objectType'),
    ]
    dumpValueClass(d, obj, props)


def qdump__panda__ecmascript__JSType(d, value):
    d: DumperBase = d
    value: DumperBase.Value = value
    d.putEnumValue(value.extractInteger(8, True), JSType.NUM_NAMES)


def qdump__panda__ecmascript__JSFunctionBase(d, value):
    obj = JSFunctionBase(value)
    props = [
        dValue(obj.Method, 'Method'),
    ]
    dumpValueClass(d, obj, props)


def qdump__panda__ecmascript__JSFunction(d, value):
    obj = JSFunction(value)
    props = [
        obj.ProtoOrDynClass,
        obj.LexicalEnv,
        obj.HomeObject,
        obj.FunctionInfoFlag,
        obj.FunctionExtraInfo,
        obj.ConstantPool,
        obj.ProfileTypeInfo,
    ]
    dumpValueClass(d, obj, [dValue(p) for p in props])

def callHelper(self, rettype, value, func, args):
    # args is a tuple.
    arg = ','.join(args)
    DumperBase.warn('PRECALL: %s -> %s(%s)' % (value.address(), func, arg))
    typename = value.type.name
    exp = '((%s*)0x%x)->%s(%s)' % (typename, value.address(), func, arg)
    DumperBase.warn('CALL: %s' % exp)
    result = self.currentContextValue.CreateValueFromExpression('', exp)
    DumperBase.warn('  -> %s' % result)
    nv = self.fromNativeValue(result)
    DumperBase.warn('NATIVE VALUE: %s' % nv)
    return nv

def qdump__std__string(d, value):
    d: DumperBase = d
    value: DumperBase.Value = value
    if value.address() != 0xFFFFFFFFFFFFFFFF:
        stdtypes.qdump__std__string(d, value)
    else:
        (_, size) = value.split('pp')
        (_, _, data) = value.split(f'pp{size}s')
        d.putValue(data.decode())
        d.putType("std::string")


def qdump__panda__ecmascript__JSMethod(d, value):
        d: DumperBase = d
        value: DumperBase.Value = value
        # d.callHelper = types.MethodType(callHelper, d)

        props = [
            lambda d: d.putCallItem('FullName', 'std::string', value, 'GetFullName', 'true'),
            # lambda d: d.putCallItem('Length', JSTaggedValue.TYPE, value, 'GetLength'),
        ]
        dumpValueHelper(d, value, props)
